package com.maxus.mini.open.api.aop;

import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.alibaba.dubbo.config.annotation.Reference;
import com.google.gson.Gson;
import com.maux.mini.data.api.user.RequestLogService;
import com.maxus.mini.common.Constants;
import com.maxus.mini.common.exception.BusinessException;
import com.maxus.mini.common.utils.MapToBeanUtil;
import com.maxus.mini.common.vo.ReqBody;
import com.maxus.mini.common.vo.ResBody;
import com.maxus.mini.entity.request.RequestLog;
import com.maxus.mini.open.api.filter.impl.InterfaceConfig;

/**
 * 调用方日志记录
 * 
 * @author wujinsong
 *
 */
@Component
@Aspect
public class LogAopAdviseDefine {
	private Logger logger = LoggerFactory.getLogger(getClass());
	@Autowired
	private Validator validator;

	@Reference(version = "1.0.0")
	private RequestLogService<RequestLog, RequestLog> requestLogService;

	@Pointcut("execution(public * com.maxus..*Controller.*(..))")
	public void pointcut() {

	}

	/**
	 * 定义一个 Pointcut, 使用 切点表达式函数 来描述对哪些 Join point 使用 advise.
	 * 
	 * @param joinPoint
	 */
	@Before("pointcut()")
	public void logMethodInvokeParam(JoinPoint joinPoint) {
		/*
		 * 获取 request 信息
		 */
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		if (attributes != null) {
			HttpServletRequest request = attributes.getRequest();
			RequestLog requestLog = (RequestLog) MapToBeanUtil.convertMap(RequestLog.class,
					this.getHeadersInfo(request));
			requestLog.setIp(this.getIpAddr(request));

			requestLogService.save(requestLog);

			try {
				Set<ConstraintViolation<RequestLog>> constraintViolations = validator.validate(requestLog);
				if (constraintViolations.size() > 0) {
					StringBuilder message = new StringBuilder();
					for (ConstraintViolation<RequestLog> constrainViolation : constraintViolations) {
						message.append(constrainViolation.getPropertyPath().toString() + constrainViolation.getMessage()
								+ "|");
					}
					throw new BusinessException(Constants.PARAM_ERROR, message.toString());
				}
			} catch (Exception e) {

			}

			Map<String, String[]> paramtersOld = request.getParameterMap();
			Map<String, String[]> paramters = new HashMap<String, String[]>();
			for (String key : paramtersOld.keySet()) {
				String[] param = paramtersOld.get(key);
				if (param[0].length() > 0) {
					paramters.put(key, param);
				}
			}

			String method = request.getHeader("method");

			Class<? extends ReqBody> clazz = InterfaceConfig.JOP_VO.get(method);
			if (null != clazz) {
				clazz = InterfaceConfig.JOP_VO.get(method);
			} else {
				throw new BusinessException(Constants.BUSI_ERROR_000007, Constants.S_BUSI_ERROR_000007);
			}

			// 参数map转化为bean
			ReqBody newBody = (ReqBody) MapToBeanUtil.convertMap(clazz, paramters);

			Set<ConstraintViolation<ReqBody>> constraintViolations = validator.validate(newBody);
			if (constraintViolations.size() > 0) {
				StringBuilder message = new StringBuilder();
				for (ConstraintViolation<ReqBody> constrainViolation : constraintViolations) {
					message.append(
							constrainViolation.getPropertyPath().toString() + constrainViolation.getMessage() + "|");
				}
				throw new BusinessException(Constants.PARAM_ERROR, message.toString());
			}
		}

		logger.info("---Before method {} invoke,param: {} ---", joinPoint.getSignature().toShortString(),
				joinPoint.getArgs());
	}

	/**
	 * 调用返回
	 * 
	 * @param joinPoint
	 * @param retVal
	 */
	@AfterReturning(pointcut = "pointcut()", returning = "retVal")
	public void logMethodInvokeResult(JoinPoint joinPoint, Object retVal) {
		logger.info("---After method {} invoke,result: {}---", joinPoint.getSignature().toShortString(),
				joinPoint.getArgs());

		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		if (attributes != null) {
			HttpServletRequest request = attributes.getRequest();
			@SuppressWarnings("unused")
			String serialno = request.getHeader("serialno");
			RequestLog requestLog = requestLogService.find(1l);

			String memo;
			try {
				memo = BeanUtils.getProperty(retVal, Constants.MEMO);
				String status = BeanUtils.getProperty(retVal, Constants.STATUS);
				requestLog.setMemo(memo);
				requestLog.setStatus(status);
			} catch (Exception e) {
				e.printStackTrace();
			}

			requestLogService.save(requestLog);
		}
	}

	/**
	 * 调用后
	 * 
	 * @param joinPoint
	 * @param exception
	 * @return
	 * @throws NoSuchMethodException
	 * @throws InvocationTargetException
	 * @throws IllegalAccessException
	 */
	@AfterThrowing(pointcut = "pointcut()", throwing = "exception")
	public void logMethodInvokeException(JoinPoint joinPoint, Exception exception) throws BusinessException, Exception {
		logger.info("---mthod {} invoke exception: {}---", joinPoint.getSignature().toShortString(),
				exception.getMessage());
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		if (attributes != null) {
			HttpServletRequest request = attributes.getRequest();
			@SuppressWarnings("unused")
			String serialno = request.getHeader("serialno");
			RequestLog requestLog = requestLogService.find(1l);

			String memo;
			try {
				memo = BeanUtils.getProperty(exception, Constants.MESSAGE);
				String status = BeanUtils.getProperty(exception, Constants.CODE);
				requestLog.setMemo(memo);
				requestLog.setStatus(status);
			} catch (Exception e) {
				e.printStackTrace();
			}

			requestLogService.save(requestLog);
		}

		ResBody resBody = new ResBody();

		String status = BeanUtils.getProperty(exception, Constants.CODE);
		String memo = BeanUtils.getProperty(exception, Constants.MESSAGE);
		resBody.setStatus(status);
		resBody.setMemo(memo);
		Gson gson = new Gson();
		this.writeContent(gson.toJson(resBody));
	}

	/**
	 * 获取请求头map
	 * 
	 * @param request
	 * @return
	 */
	private Map<String, String> getHeadersInfo(HttpServletRequest request) {
		Map<String, String> map = new HashMap<String, String>();
		Enumeration<String> headerNames = request.getHeaderNames();
		while (headerNames.hasMoreElements()) {
			String key = headerNames.nextElement();
			String value = request.getHeader(key);
			map.put(key, value);
		}
		return map;
	}

	/**
	 * 获取客户端IP
	 * 
	 * @param request
	 *            HttpServletReqeust信息
	 * @return IP
	 */
	private String getIpAddr(HttpServletRequest request) {
		String ip = request.getHeader("x-forwarded-for");

		if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("Proxy-Client-IP");
		}

		if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}

		if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getRemoteAddr();
		}

		return ip;
	}

	/**
	 * 发送异常返回
	 * 
	 * @param content
	 */
	private void writeContent(String content) {
		HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
				.getResponse();
		response.reset();
		response.setCharacterEncoding("UTF-8");
		response.setHeader("Content-Type", "application/json;charset=UTF-8");
		response.setHeader("icop-content-type", "exception");
		PrintWriter writer = null;
		try {
			writer = response.getWriter();
		} catch (IOException e) {
			e.printStackTrace();
		}
		writer.print(content);
		writer.flush();
		writer.close();
	}
}
